package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.model.search.vos.SearchArticleVo;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.SearchService;
import com.heima.utils.thread.ApThreadLocal;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    @Resource
    private RestHighLevelClient client;

    @Resource
    private ApUserSearchService apUserSearchService;

    /**
     * 搜索操作
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto dto) throws IOException {
        //2.创建搜索请求对象，并指定索引库名称
        SearchRequest searchRequest=new SearchRequest("app_info_article");

        //3.1 创建布尔查询
        BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();
        //关键字查询
        if(StringUtils.isBlank(dto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"关键字不可以为空");
        }
        boolQueryBuilder.filter(QueryBuilders.matchQuery("all",dto.getSearchWords()));

        //时间查询
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lte(dto.getMinBehotTime()));

        //3.执行查询条件操作
        searchRequest.source().query(boolQueryBuilder);

        //分页查询
        searchRequest.source().from(0).size(dto.getPageSize());

        //倒叙展示
        searchRequest.source().sort("publishTime", SortOrder.DESC);

        //高亮展示设置
        searchRequest.source().highlighter(SearchSourceBuilder.highlight()
                .field("title")
                .preTags("<font style='color: red; font-size: inherit;'>")
                .postTags("</font>")
                .requireFieldMatch(false));

        //1.执行search的api调用
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        List resultList=new ArrayList<>();

        //4.获取响应结果
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            //获取每一个map对象
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            //重置title,变成高亮对象
            HighlightField highlightField = hit.getHighlightFields().get("title");
            Text[] fragments = highlightField.getFragments();
            if(fragments!=null && fragments.length>0){//表示能够查询到高亮的域
                sourceAsMap.put("h_title",fragments[0].toString());
            }else{//表示没有查询到高亮域
                sourceAsMap.put("h_title",sourceAsMap.get("title"));
            }

            resultList.add(sourceAsMap);
        }

        //异步调用保存历史记录的接口
        if(ApThreadLocal.getApUser()!=null){
            Integer userId= ApThreadLocal.getApUser().getId();
            apUserSearchService.addHistoryToMongoDB(dto.getSearchWords(),userId);
        }


        //封装数据
        return ResponseResult.okResult(resultList);
    }

    /**
     * 新增数据到Es中
     */
    @Override
    public void addArticleToEs(SearchArticleVo searchArticleVo) {
        try {
            //2.创建索引请求对象，并指定索引名称和索引的id
            IndexRequest indexRequest=new IndexRequest("app_info_article").id(searchArticleVo.getId().toString());
            indexRequest.source(JSON.toJSONString(searchArticleVo), XContentType.JSON);
            //新增数据到Es中
            client.index(indexRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 自动补全功能
     *
     * @param userSearchDto
     * @return
     */
    @Override
    public ResponseResult searchAssociate(UserSearchDto userSearchDto) throws IOException {

        List suggestionList=new ArrayList();


        //2.创建搜索请求对象，并设置索引名称
        SearchRequest searchRequest=new SearchRequest("app_info_article");
        //3.执行自动补全设置
        searchRequest.source().suggest(
                new SuggestBuilder().addSuggestion("mySuggestion", //定义自动补全的设置名称，名称可以随意起
                        SuggestBuilders.completionSuggestion("suggestion")//表示查询的具体字段
                                .prefix(userSearchDto.getSearchWords())//传过来的参数作为前缀
                                .skipDuplicates(true) //跳过重复的
                                .size(userSearchDto.getPageSize()))//设置最多查询10条
        );
        //1.执行search查询
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //4.结果封装
        Suggest suggest = response.getSuggest();
        CompletionSuggestion mySuggestion = suggest.getSuggestion("mySuggestion");
        List<CompletionSuggestion.Entry.Option> options = mySuggestion.getOptions();
        if(options!=null && options.size()>0){
            for (CompletionSuggestion.Entry.Option option : options) {
                String s = option.getText().toString();
                suggestionList.add(s);
            }
        }


        return ResponseResult.okResult(suggestionList);
    }
}